import tkinter as tk
import traceback
from tkinter import filedialog
import os
import re
import time
import subprocess
import requests
import hashlib
import sys
import base64
import threading
from wsgiref.simple_server import server_version
from datetime import datetime
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend
import requests
import json
import re
import tkinter as tk
from tkinter import messagebox
from http.server import ThreadingHTTPServer, SimpleHTTPRequestHandler
#from HWID import HWID_Start #导入HWID模块


# 从一行文本中提取坐标信息
def extract_coordinates(line):
    if line.startswith("fill"):
        parts = line.split()
        if len(parts) >= 7:
            x1_str = parts[1][1:]
            z1_str = parts[3][1:]
            x2_str = parts[4][1:]
            z2_str = parts[6][1:]
            try:
                x1 = float(x1_str)
                z1 = float(z1_str)
                x2 = float(x2_str)
                z2 = float(z2_str)
                return x1, z1, x2, z2
            except ValueError:
                print(f"坐标转换失败: x1={x1_str}, z1={z1_str}, x2={x2_str}, z2={z2_str}")
    elif line.startswith("setblock"):
        parts = line.split()
        if len(parts) >= 4:
            x_str = parts[1][1:]
            z_str = parts[3][1:]
            try:
                x = float(x_str)
                z = float(z_str)
                return x, z, x, z
            except ValueError:
                print(f"坐标转换失败: x={x_str}, z={z_str}")
    return None, None, None, None

# 获取所有坐标中的最大和最小x、z值
def get_max_min_coords(lines):
    all_x = []
    all_z = []
    for line in lines:
        x1, z1, x2, z2 = extract_coordinates(line)
        if x1 is not None and z1 is not None and x2 is not None and z2 is not None:
            all_x.extend([x1, x2])
            all_z.extend([z1, z2])
    if not all_x or not all_z:
        return None, None, None, None
    min_x = min(all_x)
    max_x = max(all_x)
    min_z = min(all_z)
    max_z = max(all_z)
    return min_x, max_x, min_z, max_z

# 将指定范围的坐标划分为16x16的区块
def divide_into_16x16(min_x, max_x, min_z, max_z):
    start_x_int = int(min_x // 1)
    end_x_int = int(max_x // 1) if max_x.is_integer() else int(max_x // 1) + 1
    start_z_int = int(min_z // 1)
    end_z_int = int(max_z // 1) if max_z.is_integer() else int(max_z // 1) + 1
    result = []
    for start_x in range(start_x_int, end_x_int + 1, 16):
        end_x = min(start_x + 15, end_x_int)
        for start_z in range(start_z_int, end_z_int + 1, 16):
            end_z = min(start_z + 15, end_z_int)
            result.append((start_x, end_x, start_z, end_z))
    return result

# 将指定范围的坐标划分为32x32的区块
def divide_into_32x32(min_x, max_x, min_z, max_z):
    start_x_int = int(min_x // 1)
    end_x_int = int(max_x // 1) if max_x.is_integer() else int(max_x // 1) + 1
    start_z_int = int(min_z // 1)
    end_z_int = int(max_z // 1) if max_z.is_integer() else int(max_z // 1) + 1
    result = []
    for start_x in range(start_x_int, end_x_int + 1, 32):
        end_x = min(start_x + 31, end_x_int)
        for start_z in range(start_z_int, end_z_int + 1, 32):
            end_z = min(start_z + 31, end_z_int)
            result.append((start_x, end_x, start_z, end_z))
    return result

# 按y坐标对命令行进行排序
def sort_lines_by_y(lines):
    def get_sort_key(line):
        if line.startswith("fill"):
            parts = line.split()
            if len(parts) >= 4:
                y_str = parts[2][1:]
                try:
                    y = float(y_str)
                    return (y, 0)  # 0 表示 fill 命令
                except ValueError:
                    pass
        elif line.startswith("setblock"):
            parts = line.split()
            if len(parts) >= 3:
                y_str = parts[2][1:]
                try:
                    y = float(y_str)
                    return (y, 1)  # 1 表示 setblock 命令
                except ValueError:
                    pass
        return (0, 2)  # 默认值

    return sorted(lines, key=get_sort_key)

# 对坐标进行减法运算，将坐标转换为相对坐标
def subtract_coordinates(lines, tp_x, tp_z):
    new_lines = []
    for line in lines:
        if line.startswith("setblock"):
            parts = line.split()
            x = float(parts[1][1:]) - tp_x
            y = float(parts[2][1:])
            z = float(parts[3][1:]) - tp_z
            new_line = f"setblock ~{x:.1f} ~{y:.1f} ~{z:.1f} {' '.join(parts[4:])}"
            new_lines.append(new_line)
        elif line.startswith("fill"):
            parts = line.split()
            x1 = float(parts[1][1:]) - tp_x
            y1 = float(parts[2][1:])
            z1 = float(parts[3][1:]) - tp_z
            x2 = float(parts[4][1:]) - tp_x
            y2 = float(parts[5][1:])
            z2 = float(parts[6][1:]) - tp_z
            new_line = f"fill ~{x1:.1f} ~{y1:.1f} ~{z1:.1f} ~{x2:.1f} ~{y2:.1f} ~{z2:.1f} {' '.join(parts[7:])}"
            new_lines.append(new_line)
        else:
            new_lines.append(line)
    return new_lines

# 对区块进行S型排序
def s_sort_regions(regions):
    x_groups = {}
    for region in regions:
        start_x, _, start_z, _ = region
        x_group = start_x // 16 if len(regions[0]) == 4 and regions[0][1] - regions[0][0] == 15 else start_x // 32
        if x_group not in x_groups:
            x_groups[x_group] = []
        x_groups[x_group].append(region)

    sorted_regions = []
    for x_group in sorted(x_groups.keys()):
        z_coords = [r[2] for r in x_groups[x_group]]
        if x_group % 2 == 0:
            sorted_z_regions = sorted(x_groups[x_group], key=lambda r: r[2])
        else:
            sorted_z_regions = sorted(x_groups[x_group], key=lambda r: -r[2])
        sorted_regions.extend(sorted_z_regions)
    return sorted_regions

# 解析方块的状态字符串
def parse_state_string(block):
    state_string = ""
    if "states" in block:
        states = block["states"]
        for k, v in states.items():
            if state_string != "":
                state_string += ","
            if isinstance(v, str) and re.match(r"^\[.*\]$", v):
                state_string += f"\"{k}\":{v}"
            elif isinstance(v, int) or isinstance(v, float):
                state_string += f"\"{k}\":{v}"
            elif isinstance(v, str):
                state_string += f"\"{k}\":\"{v}\""
    return state_string

# 判断指定区域内的方块是否连续且相同
def is_continuous_region(blocks, x1, y1, z1, x2, y2, z2, block_name, state_string):
    for x in range(x1, x2 + 1):
        for y in range(y1, y2 + 1):
            for z in range(z1, z2 + 1):
                if (x, y, z) not in blocks or blocks[(x, y, z)][0] != block_name or blocks[(x, y, z)][
                    1] != state_string:
                    return False
    return True

# 找到最大的连续区域
def find_largest_region(blocks, start, block_name, state_string):
    x, y, z = start
    x_end, y_end, z_end = x, y, z

    while is_continuous_region(blocks, x, y, z, x_end + 1, y_end, z_end, block_name, state_string):
        x_end += 1
    while is_continuous_region(blocks, x, y, z, x_end, y_end + 1, z_end, block_name, state_string):
        y_end += 1
    while is_continuous_region(blocks, x, y, z, x_end, y_end, z_end + 1, block_name, state_string):
        z_end += 1

    return (x, y, z), (x_end, y_end, z_end)

# 拆分fill命令，避免命令过长
def split_fill_command(start, end, block_name, state_string):
    x1, y1, z1 = start
    x2, y2, z2 = end
    block_count = (x2 - x1 + 1) * (y2 - y1 + 1) * (z2 - z1 + 1)
    if block_count <= 32367:
        command = f"fill ~{x1:.1f} ~{y1:.1f} ~{z1:.1f} ~{x2:.1f} ~{y2:.1f} ~{z2:.1f} {block_name}"
        if state_string:
            command += f" {state_string}"
        return [command]

    commands = []
    step = max(1, int(block_count ** (1 / 3)))
    for i in range(x1, x2 + 1, step):
        for j in range(y1, y2 + 1, step):
            for k in range(z1, z2 + 1, step):
                sub_x2 = min(i + step - 1, x2)
                sub_y2 = min(j + step - 1, y2)
                sub_z2 = min(k + step - 1, z2)
                commands.extend(split_fill_command((i, j, k), (sub_x2, sub_y2, sub_z2), block_name, state_string))
    return commands

# 找到所有的fill区域并生成相应的命令
def find_fill_regions(blocks):
    fill_commands = []
    setblock_commands = []
    block_count_3d = 0
    block_count_2d = 0
    block_count_1d = 0
    single_block_count = 0

    visited = set()
    for (x, y, z), (block_name, state_string) in blocks.items():
        if (x, y, z) in visited:
            continue

        start = (x, y, z)
        end = find_largest_region(blocks, start, block_name, state_string)
        x_end, y_end, z_end = end[1]

        region_size = (x_end - x + 1) * (y_end - y + 1) * (z_end - z + 1)
        if region_size > 1:
            fill_commands.extend(split_fill_command(start, end[1], block_name, state_string))
            if (x_end - x + 1) > 1 and (y_end - y + 1) > 1 and (z_end - z + 1) > 1:
                block_count_3d += region_size
            elif (x_end - x + 1) > 1 and (y_end - y + 1) > 1 or (z_end - z + 1) > 1:
                block_count_2d += region_size
            else:
                block_count_1d += region_size
        else:
            single_block_count += 1
            setblock_commands.append(f"setblock ~{x:.1f} ~{y:.1f} ~{z:.1f} {block_name} {state_string}")

        for x_visited in range(x, x_end + 1):
            for y_visited in range(y, y_end + 1):
                for z_visited in range(z, z_end + 1):
                    visited.add((x_visited, y_visited, z_visited))

    return fill_commands, setblock_commands, block_count_3d, block_count_2d, block_count_1d, single_block_count

# 优化指定区域的命令行
def optimize_region_lines(region_lines):
    blocks = {}
    for line in region_lines:
        if line.startswith('setblock'):
            parts = line.strip().split()
            x = int(float(parts[1][1:]))
            y = int(float(parts[2][1:]))
            z = int(float(parts[3][1:]))
            block_name = parts[4]
            if len(parts) > 5:
                state_string = ' '.join(parts[5:])
            else:
                state_string = ""
            blocks[(x, y, z)] = (block_name, state_string)
        elif line.startswith('fill'):
            parts = line.strip().split()
            x1 = int(float(parts[1][1:]))
            y1 = int(float(parts[2][1:]))
            z1 = int(float(parts[3][1:]))
            x2 = int(float(parts[4][1:]))
            y2 = int(float(parts[5][1:]))
            z2 = int(float(parts[6][1:]))
            block_name = parts[7]
            if len(parts) > 8:
                state_string = ' '.join(parts[8:])
            else:
                state_string = ""
            for x in range(x1, x2 + 1):
                for y in range(y1, y2 + 1):
                    for z in range(z1, z2 + 1):
                        blocks[(x, y, z)] = (block_name, state_string)

    fill_commands, setblock_commands, _, _, _, _ = find_fill_regions(blocks)
    optimized_lines = fill_commands + setblock_commands
    return optimized_lines

# 删除tp命令
def remove_tp_commands(lines):
    return [line for line in lines if not line.startswith("tp")]

# 将fill命令转换为setblock命令
def convert_fill_to_setblock(lines):
    new_lines = []
    for line in lines:
        if line.startswith("fill"):
            parts = line.split()
            x1 = float(parts[1][1:])
            y1 = float(parts[2][1:])
            z1 = float(parts[3][1:])
            x2 = float(parts[4][1:])
            y2 = float(parts[5][1:])
            z2 = float(parts[6][1:])
            block_name = parts[7]
            state_string = ' '.join(parts[8:]) if len(parts) > 8 else ""
            for x in range(int(x1), int(x2) + 1):
                for y in range(int(y1), int(y2) + 1):
                    for z in range(int(z1), int(z2) + 1):
                        new_lines.append(f"setblock ~{x:.1f} ~{y:.1f} ~{z:.1f} {block_name} {state_string}")
        else:
            new_lines.append(line)
    return new_lines

def main():
    root = tk.Tk()
    root.withdraw()
    file_path = filedialog.askopenfilename()
    if not file_path:
        print("未选择文件。")
        return

    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            lines = file.readlines()
            lines = [line.replace('/', '') for line in lines]

        lines = remove_tp_commands(lines)
        lines = convert_fill_to_setblock(lines)

    except Exception as e:
        print(f"读取文件时发生错误: {e}")
        return

    print('请选择坐标模式：')
    print('1. 固定坐标')
    print('2. 任意坐标')
    coord_mode = input("请输入选项 (1 或 2): ")

    if coord_mode == '1':
        Back_X = float(input('请输入返回X:'))
        Back_Z = float(input('请输入返回Z:'))
        mode_text = "固定坐标"
    else:
        Back_X = 0
        Back_Z = 0
        mode_text = "任意坐标"

    print('请选择区块划分方式：')
    print('1. 16x16 区块')
    print('2. 32x32 区块')
    choice = input("请输入选项 (1 或 2): ")

    # 询问tp命令后插入的命令数量
    insert_count_input = input("tp命令插入命令数量(默认为0): ")
    insert_count = int(insert_count_input) if insert_count_input.strip() else 0

    start_time = time.time()
    print('正在分区和三维优化中...')

    try:
        min_x, max_x, min_z, max_z = get_max_min_coords(lines)
        total_original_commands = 0
        total_optimized_commands = 0
        tp_count = 0

        if min_x is not None:
            if choice == '1':
                regions = divide_into_16x16(min_x, max_x, min_z, max_z)
                block_size = "16x"
            elif choice == '2':
                regions = divide_into_32x32(min_x, max_x, min_z, max_z)
                block_size = "32x"
            else:
                print("无效的选择，请输入 1 或 2。")
                return

            file_name, file_ext = os.path.splitext(file_path)
            output_file_name = f"{file_name}-区块化-区块数量-{len(regions)}-区块大小-{block_size}-模式-{mode_text}{file_ext}"
            output_file_path = os.path.join(os.path.dirname(file_path), output_file_name)

            prev_x = 0
            prev_z = 0
            output_lines = []
            with open(output_file_path, 'w', encoding='utf-8') as output_file:
                sorted_regions = s_sort_regions(regions)
                for start_x, end_x, start_z, end_z in sorted_regions:
                    region_lines = []
                    for line in lines:
                        x1, z1, x2, z2 = extract_coordinates(line)
                        if x1 is not None and z1 is not None and x2 is not None and z2 is not None:
                            if (start_x <= x1 <= end_x and start_z <= z1 <= end_z) or (start_x <= x2 <= end_x and start_z <= z2 <= end_z):
                                region_lines.append(line)
                    if region_lines:
                        if coord_mode == '1':
                            output_lines.append(f"tp {Back_X:.1f} ~ {Back_Z:.1f}")
                            output_lines.append(f"tp ~{start_x:.1f} ~ ~{start_z:.1f}")
                        else:
                            relative_x = start_x - prev_x
                            relative_z = start_z - prev_z
                            output_lines.append(f"tp ~{relative_x:.1f} ~ ~{relative_z:.1f}")
                            prev_x = start_x
                            prev_z = start_z

                        tp_count += 1
                        sorted_lines = sort_lines_by_y(region_lines)
                        subtracted_lines = subtract_coordinates(sorted_lines, start_x, start_z)
                        total_original_commands += len(subtracted_lines)
                        optimized_lines = optimize_region_lines(subtracted_lines)
                        sorted_optimized_lines = sort_lines_by_y(optimized_lines)
                        total_optimized_commands += len(sorted_optimized_lines)
                        output_lines.extend(sorted_optimized_lines)

            # 在tp命令后插入指定数量的命令
            final_lines = []
            for line in output_lines:
                final_lines.append(line)
                if line.startswith("tp"):
                    for _ in range(insert_count):
                        final_lines.append("testfor @s")

            # 将最终结果写入文件
            with open(output_file_path, 'w', encoding='utf-8') as output_file:
                for line in final_lines:
                    output_file.write(line + '\n')

            command_reduction = total_original_commands - total_optimized_commands
            reduction_percentage = (
                                           command_reduction / total_original_commands) * 100 if total_original_commands > 0 else 0

            end_time = time.time()
            elapsed_time = end_time - start_time

            print(f"文件已保存到 {output_file_path}")
            print(f"优化情况：")
            print(f"原始指令数量: {total_original_commands}")
            print(f"优化后指令数量: {total_optimized_commands}")
            print(f"减少的指令数量: {command_reduction}")
            print(f"减少的百分比: {reduction_percentage:.2f}%")
            print(f"区块数量: {tp_count}")
            print(f"使用时间: {elapsed_time:.2f} 秒")
            time.sleep(1)
            input('按下回车结束进程...')
        else:
            print("未找到有效的坐标。")
    except Exception as e:
        print(f"发生错误: {e}")

if __name__ == "__main__":
    #HWID_Start() #en这是hwid模块 已删除
    print("制作者:lvcai_tang 版本:V 2.1\nTips:请不要选择已经区块化过的文件不然可能出现转换问题\nV 2.1 更新内容\n1.自动处理已三维优化过的建筑(人话就是可以区块化三维过的)\n2.添加tp后插入命令行(用于tp延迟的操作)\n3.开源免费公益,禁止商用")
    main()
